/**
 * Copyright (C) 2012 The SkyTvOS Project
 * <p/>
 * Version     Date           Author
 * ─────────────────────────────────────
 * 2016年11月9日         yellowlgx
 */

package com.skyworth.ui.newlistview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.Log;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.OverScroller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class NewHorizontalGridView extends FrameLayout
        implements OnFocusChangeListener, OnClickListener, OnKeyListener
{
    private int SCROLL_DURING = 250;

    private OverScroller mOverScroller = null;
    private int mHorizontalSpace = 0, mVerticalSpace = 0;
    private int mNumRows = 0;
    private int mChildHeight = 0, mChildWidth = 0;
    private int mGridHeight = 0, mGridWidth = 0;

    private int mFirstPostion = 0, mLastPosition = 0;
    private int mCurSelectPosition;

    private int maxScreenChildCount = 0, mOneScreenLineCount = 0;

    private BaseAdapter mAdapter;
    private AdapterDataSetObserver mDataSetObserver;
    private int mItemCount;
    private int mOldItemCount;

    // 想更多了解ListView视图如何复用可以看AbsListView内部类RecycleBin
    private RecycleBin mRecycleBin;
    private Map<Integer, View> mViewLists;
    private List<View> mNeedRefreshViews;

    private Rect mTempRect = new Rect();
    private int scrollOffset = 0;
    private int mInitOffset = 0;

    private boolean isScrollBarScroll = false, doScroll = false;

    private boolean[] mIsScrap = new boolean[1];

    private OnHItemSelectedListener mItemSelectListener;

    private OnHItemOnKeyListener mItemOnKeyListener;

    /**
     * @Fields mItemClickListener 子Item点击事件回调
     */
    private OnHItemClickListener mItemClickListener;

    /**
     * @Fields scrollListener 当前滚动状态接口监听，目前只提供滚动结束回调
     */
    private OnHScrollStateListener mScrollListener;

    /**
     * @ClassName OnItemSelectedListener
     * @Description 子Item选中和未选中接口
     */
    public interface OnHItemSelectedListener
    {
        public void onHItemSelected(NewHorizontalGridView parent, View selectView, int selectPosition);

        public void onHItemUnSelected(NewHorizontalGridView parent, View unSelectView);
    }

    /**
     * @author yellowlgx
     * @ClassName OnBorderItemOnKeyListener
     * @Description Item上按键回调
     */
    public interface OnHItemOnKeyListener
    {
        /**
         * @param view
         * @param keyCode
         * @param position
         * @return boolean
         * @Description 边界Item按键回调，主要处理边界上下左右按键<br/>
         */
        public boolean onHBorderItemOnKeyEvent(View view, int keyCode, int position);

        /**
         * @param view
         * @param keyCode
         * @param position
         * @return boolean
         * @Description Item上按键回调，过滤了本身需要被处理的上下左右，其余按键均会回调<br/>
         */
        public boolean onHItemOnKeyEvent(View view, int keyCode, int position);
    }

    public interface OnHItemClickListener
    {
        void onHItemClick(NewHorizontalGridView parent, View view, int position);
    }

    /**
     * @ClassName OnScrollStateListener
     * @Description 滚动状态接口，提供滚动结束后当前现实中第一个位置和最后一个位置<br/>
     * 用于外部需要更新数据时，当前显示的是第几个数据
     */
    public interface OnHScrollStateListener
    {
        /**
         * @Description 滚动开始时的回调<br/>
         */
        public void onHScrollStart();

        /**
         * @param parent       当前ListView
         * @param firstPostion 当前显示中的第一个位置，也就是数据List中的具体位置
         * @param endPosition  当前现显示中的最后一个位置，也就是数据使用到的最后位置 void
         * @Description 滚动结束时的回调<br/>
         */
        public void onHScrollEnd(NewHorizontalGridView parent, int firstPostion, int endPosition);
    }

    public NewHorizontalGridView(Context context)
    {
        super(context);
        init();
    }

    private void init()
    {
        mOverScroller = new OverScroller(getContext(), new DecelerateInterpolator());
        mRecycleBin = new RecycleBin();
    }

    public void setOnHItemSelectedListener(OnHItemSelectedListener listener)
    {
        mItemSelectListener = listener;
    }

    public void setOnHItemOnKeyListener(OnHItemOnKeyListener listener)
    {
        mItemOnKeyListener = listener;
    }

    public void setOnHItemClickListener(OnHItemClickListener listener)
    {
        mItemClickListener = listener;
    }

    public void setOnHScrollStateListener(OnHScrollStateListener listener)
    {
        mScrollListener = listener;
    }

    public void setRows(int row)
    {
        mNumRows = row;
    }

    public void setItemWidth(int itemWidth)
    {
        mChildWidth = itemWidth;
    }

    public void setItemHeight(int itemHeight)
    {
        mChildHeight = itemHeight;
    }

    public void setGridWidth(int gridWidth)
    {
        mGridWidth = gridWidth;
    }

    public void setGridHeight(int gridHeight)
    {
        mGridHeight = gridHeight;
    }

    public void setHorizontalSpace(int horizontalSpace)
    {
        mHorizontalSpace = horizontalSpace;
    }

    public void setVerticalSpace(int verticalSpace)
    {
        mVerticalSpace = verticalSpace;
        scrollOffset = mVerticalSpace;
    }

    /**
     * @param duration void
     * @Description 设置单行滚动时间<br/>
     * @date 2016-1-5
     */
    public void setScrollDuration(int duration)
    {
        SCROLL_DURING = duration;
    }

    public BaseAdapter getAdapter()
    {
        return mAdapter;
    }

    public int getLastPosition()
    {
        return mLastPosition;
    }

    private int[] getFirstViewLocation()
    {
        int[] location = new int[2];
        getFirstVisibleView().getLocationInWindow(location);
        return location;
    }

    /**
     * @return int
     * @Description 获取当前List中第一个可见View的position<br/>
     * @date 2016-1-6
     */
    public int getFirstPosition()
    {
        if (mNumRows == 0)
            return 0;
        int firstPosition = getFirstVisibleView() != null ? getFirstVisibleView().getId()
                : mFirstPostion;
        if (firstPosition % mNumRows != 0)
            firstPosition -= firstPosition % mNumRows;
        return firstPosition;
    }

    /**
     * @return View
     * @Description 获取当前list中第一个可见的View<br/>
     * @date 2016-1-6
     */
    public View getFirstVisibleView()
    {
        int count = this.getChildCount();
        int[] temp = new int[count];
        for (int i = 0; i < count; i++)
        {
            View view = this.getChildAt(i);
            int id = view.getId();
            temp[i] = id;
        }
        Arrays.sort(temp);
        for (int i = 0; i < temp.length; i++)
        {
            View view = this.findViewById(temp[i]);
            int[] location = new int[2];
            view.getLocationInWindow(location);
            if (location[0] >= 0)
            {
                return view;
            }
        }
        return null;
    }

    public View getSelectedView()
    {
        return this.getFocusedChild();
    }

    public int getSelectedItemPosition()
    {
        return mCurSelectPosition;
    }

    public void setSelection(int position)
    {
        setSelectionNeedFocus(position, true, 0);
    }

    /**
     * @param position
     * @return int
     * @Description 获取当前位置所在列数<br/>
     */
    public int getColumn(int position)
    {
        int temp = (position + 1) / mNumRows;
        int mode = (position + 1) % mNumRows;
        if (mode != 0)
        {
            temp++;
        }
        return temp;
    }

    /**
     * @param position
     * @return boolean
     * @Description 判断当前position是否处于最后一列<br/>
     */
    public boolean isLastColumn(int position)
    {
        int current = getColumn(position);
        int total = getColumn(getAdapter().getCount() - 1);
        if (current == total)
            return true;
        return false;
    }

    private void setSelectionNeedFocus(final int position, final boolean needFocus, int duration)
    {
        if (mAdapter == null || mAdapter.getCount() <= 0)
        {
            throw new UnsupportedOperationException("Adapter is null!!!");
        }
        if (position > mAdapter.getCount() - 1 || position < 0)
        {
            throw new IndexOutOfBoundsException(
                    "Invalid index " + position + ", size is " + (mAdapter.getCount() - 1));
        }
        int tempPosition = position - getFirstPosition();
        int temRowCount = position;
        if (position < getFirstPosition() && isScrollBarScroll)
            temRowCount += mNumRows;
        if (position < mNumRows)
            temRowCount = mNumRows;
        int tempRow = getColumn(Math.abs(temRowCount - getFirstPosition()));
        int moveCol = 0;
        if (tempPosition > 0)
        {
            if (tempRow < mNumRows)
            {
                moveCol = tempRow;
            } else
            {
                moveCol = tempRow - 1;
            }
        } else
        {
            moveCol = tempRow;
        }
        if (position > getColumn(getAdapter().getCount() - 1) * mNumRows
                - mOneScreenLineCount * mNumRows)
        {
            moveCol -= (mOneScreenLineCount - 1);
        }
        View view = this.findViewById(position);
        int[] location = new int[2];
        int[] gridLocation = new int[2];
        this.getLocationInWindow(gridLocation);
        if (view != null)
        {
            // 滚动
            view.getLocationInWindow(location);
            int viewRight = location[0] + mChildWidth;
            if (tempPosition > 0)
            {
                if (viewRight > this.getWidth())
                {
                } else
                {
                    if (!view.hasFocus() && needFocus)
                        view.requestFocus();
                }
            } else if (tempPosition < 0)
            {
                if (location[0] < 0)
                {
                } else
                {
                    if (!view.hasFocus() && needFocus)
                        view.requestFocus();
                }
            } else
            {
                if (!view.hasFocus() && needFocus)
                    view.requestFocus();
                return;
            }
            scrollToChild(view, needFocus);
        } else
        {
            int scrollSize = 0;
            if (tempPosition > 0)
            {
                scrollSize = (mChildWidth + mHorizontalSpace) * moveCol;

            } else if (tempPosition < 0)
            {
                scrollSize = -(mChildWidth + mHorizontalSpace) * moveCol;
                if (getFirstViewLocation()[0] > gridLocation[0])
                    scrollSize += (getFirstViewLocation()[0] - gridLocation[0] + mHorizontalSpace);
            } else
            {
                return;
            }
            if (moveCol > 8)
            {
                setAdapter(mAdapter, position);
            } else
            {
                if (duration != 0)
                    doScrollX(scrollSize, duration);
                else
                    doScrollX(scrollSize);
            }

            this.postDelayed(new Runnable()
            {
                @Override
                public void run()
                {
                    View focusView = mViewLists.get(position);
                    if (focusView != null && !focusView.hasFocus() && needFocus)
                        focusView.requestFocus();
                }
            }, 300);
        }
    }

    public void setAdapter(BaseAdapter adapter)
    {
        if (adapter == null)
        {
            throw new NullPointerException("adapter is null !!!!!");
        }
        if (mAdapter != null && mDataSetObserver != null)
            mAdapter.unregisterDataSetObserver(mDataSetObserver);

        mAdapter = adapter;
        resetGrid();
        mRecycleBin.setViewTypeCount(mAdapter.getViewTypeCount());

        if (mDataSetObserver == null)
            mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        mAdapter.notifyDataSetChanged();
    }

    public void setAdapter(BaseAdapter adapter, int startPosition)
    {
        if (adapter == null)
        {
            throw new NullPointerException("adapter is null !!!!!");
        }
        if (mAdapter != null && mDataSetObserver != null)
            mAdapter.unregisterDataSetObserver(mDataSetObserver);

        mAdapter = adapter;
        resetGrid();
        mRecycleBin.setViewTypeCount(mAdapter.getViewTypeCount());

        final int mode = startPosition % mNumRows;
        mFirstPostion = startPosition - mode;
        int oneScreenTotal = mOneScreenLineCount * mNumRows;
        int lastColumn = getColumn(getAdapter().getCount() - 1);
        if (mFirstPostion + oneScreenTotal >= mAdapter.getCount() - 1)
            mFirstPostion -= (mOneScreenLineCount - (lastColumn - getColumn(mFirstPostion)) - 1)
                    * mNumRows;

        if (mDataSetObserver == null)
            mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        mAdapter.notifyDataSetChanged();
    }

    private void scrollToChild(View child, boolean needFocus)
    {
        if (child == null)
            return;
        child.getDrawingRect(mTempRect);

        /* Offset from child's local coordinates to ScrollView coordinates */
        offsetDescendantRectToMyCoords(child, mTempRect);

        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);

        if (scrollDelta != 0)
        {
            doScrollX(scrollDelta);
        }
        if (!child.hasFocus() && needFocus)
        {
            child.requestFocus();
        }
    }

    @SuppressLint("UseSparseArrays")
    private void resetGrid()
    {
        this.removeAllViews();
        scrollTo(0, 0);
        mOneScreenLineCount = mGridWidth / (mChildWidth + mHorizontalSpace);
        mFirstPostion = 0;
        mLastPosition = 0;
        mCurSelectPosition = 0;
        mItemCount = 0;
        mOldItemCount = 0;
        mRecycleBin.clear();
        if (mViewLists != null)
        {
            mViewLists.clear();
            mViewLists = null;
        }
        mViewLists = new HashMap<Integer, View>();

        if (mNeedRefreshViews != null)
        {
            mNeedRefreshViews.clear();
            mNeedRefreshViews = null;
        }
        mNeedRefreshViews = new ArrayList<View>();
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event)
    {
        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
    }

    long lastOnKeyTime = 0;
    int downRepeatCount = 0;
    boolean mCanScroll = true;

    private boolean executeKeyEvent(KeyEvent event)
    {
        if (event.getAction() == KeyEvent.ACTION_DOWN)
        {
            mTempRect.setEmpty();
            long curTime = System.currentTimeMillis();
            downRepeatCount = event.getRepeatCount();
            Log.i("lgx", "getChildCount------>  " + this.getChildCount());
            switch (event.getKeyCode())
            {
                case KeyEvent.KEYCODE_DPAD_LEFT:
                    if (getColumn(mCurSelectPosition) == 1)
                    {
                        int size = mNeedRefreshViews.size();
                        for (int i = 0; i < size; i++)
                        {
                            NewAdapterItem item = (NewAdapterItem) mNeedRefreshViews.get(i);
                            if (item != null)
                            {
                                item.refreshItemUI();
                            }
                        }
                        mNeedRefreshViews.clear();
                        if (mItemOnKeyListener != null)
                            return mItemOnKeyListener.onHBorderItemOnKeyEvent(getSelectedView(), event
                                    .getKeyCode(), mCurSelectPosition);
                        return true;
                    }
                    if (!mCanScroll)
                        return true;
                    if (Math.abs(curTime - lastOnKeyTime) < 80)
                        return true;
                    lastOnKeyTime = curTime;
                    return arrowScroll(FOCUS_LEFT);

                case KeyEvent.KEYCODE_DPAD_RIGHT:
                    if (isLastColumn(mCurSelectPosition))
                    {
                        int size = mNeedRefreshViews.size();
                        for (int i = 0; i < size; i++)
                        {
                            NewAdapterItem item = (NewAdapterItem) mNeedRefreshViews.get(i);
                            if (item != null)
                            {
                                item.refreshItemUI();
                            }
                        }
                        mNeedRefreshViews.clear();
                        if (mItemOnKeyListener != null)
                            return mItemOnKeyListener.onHBorderItemOnKeyEvent(getSelectedView(), event
                                    .getKeyCode(), mCurSelectPosition);
                        return true;
                    }
                    if (!mCanScroll)
                        return true;
                    if (Math.abs(curTime - lastOnKeyTime) < 80)
                        return true;
                    lastOnKeyTime = curTime;
                    return arrowScroll(FOCUS_RIGHT);
            }
        } else if (event.getAction() == KeyEvent.ACTION_UP)
        {
            if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT || event.getKeyCode() == KeyEvent
                    .KEYCODE_DPAD_RIGHT)
            {
                Log.w("lgx", "mOverScroller.isFinished()------>  " + mOverScroller.isFinished());
                Log.w("lgx", "downRepeatCount------>  " + downRepeatCount);
                if (!mOverScroller.isFinished() && downRepeatCount > 1)
                {
                    mOverScroller.abortAnimation();
                    Log.w("lgx", "mOverScroller.getFinalX()------>  " + mOverScroller.getFinalX());
                    scrollTo(mOverScroller.getFinalX(), mOverScroller.getFinalY());
                    if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT)
                    {
                        fillLeft();
                    } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT)
                    {
                        fillRight();
                    }
                }
                if (mOverScroller.isFinished() && downRepeatCount > 1)
                {
                    mCanScroll = false;
                    this.postDelayed(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            mCanScroll = true;
                        }
                    }, 300);
                }

//                this.postDelayed(new Runnable()
//                {
//                    @Override
//                    public void run()
//                    {
//                        Log.i("lgx", "mNeedRefreshViews.size ==   " + mNeedRefreshViews.size());
//                        int size = mNeedRefreshViews.size();
//                        for (int i = 0; i < size; i++)
//                        {
//                            NewAdapterItem item = (NewAdapterItem) mNeedRefreshViews.get(i);
//                            if (item != null)
//                            {
//                                item.refreshItemUI();
//                            }
//                        }
//                        mNeedRefreshViews.clear();
//                    }
//                }, SCROLL_DURING);
            }
        }
        return false;
    }

    public boolean arrowScroll(int direction)
    {
        View currentFocused = findFocus();
        if (currentFocused == this)
            currentFocused = null;
        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
        if (nextFocused != null)
        {
            doScroll = true;
            nextFocused.getDrawingRect(mTempRect);
            offsetDescendantRectToMyCoords(nextFocused, mTempRect);
            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
            if (scrollDelta == 0)
            {
                doScroll = false;
                nextFocused.requestFocus(direction);
                return true;
            }
            doScrollX(scrollDelta);
            nextFocused.requestFocus(direction);
        } else
        {
            return true;
        }
        return true;
    }

    private void doScrollX(int x)
    {
        if (x != 0)
            doScroll = true;
        mOverScroller.startScroll(getScrollX(), 0, x, 0, SCROLL_DURING);
        invalidate();
    }

    private void doScrollX(int x, int duration)
    {
        mOverScroller.startScroll(getScrollX(), 0, x, 0, duration);
        invalidate();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt)
    {
        super.onScrollChanged(l, t, oldl, oldt);
    }

    private void trackMotionScroll(int incrementalDeltaX)
    {
        final boolean right = incrementalDeltaX > 0; // true 表示按左键，向右滑动，false反之
        int childCount = getChildCount();
        final int absIncrementalDeltaX = Math.abs(incrementalDeltaX);
        if (absIncrementalDeltaX < (mChildWidth + mHorizontalSpace) || childCount == 0)
        {
            if (absIncrementalDeltaX > mChildWidth)
                fillGap(right);
            return;
        }
        if (mFirstPostion == 0 && right)
            return;
        if (mLastPosition == mItemCount - 1 && !right)
            return;

        int width = mGridWidth;
        int screenLeft = getScrollX() - (mChildWidth + mHorizontalSpace);
        int screenRight = screenLeft + width + 2 * (mChildWidth + mHorizontalSpace);
        Rect removRect = new Rect();

        if (right)
        {
            for (int i = 0; i < childCount; i++)
            {
                final View child = this.findViewById(mLastPosition);
                if (child == null)
                    break;
                child.getDrawingRect(removRect);
                offsetDescendantRectToMyCoords(child, removRect);
                if (removRect.left > screenRight)
                {
                    child.setOnClickListener(null);
                    child.setOnKeyListener(null);
                    child.setOnFocusChangeListener(null);
                    mViewLists.remove(mLastPosition);
//                    if (mNeedRefreshViews.contains(child))
                    mNeedRefreshViews.remove(child);
                    child.setId(-1);
                    detachViewFromParent(child);
                    mRecycleBin.addScrapView(((NewBaseItem) child).mViewType, child);
                    mLastPosition--;
                }
            }
        } else
        {
            for (int i = 0; i < childCount; i++)
            {
                final View child = this.findViewById(mFirstPostion);
                if (child == null)
                    break;
                child.getDrawingRect(removRect);
                offsetDescendantRectToMyCoords(child, removRect);
                if (removRect.right < screenLeft)
                {
                    child.setOnClickListener(null);
                    child.setOnKeyListener(null);
                    child.setOnFocusChangeListener(null);
                    mViewLists.remove(mFirstPostion);
//                    if (mNeedRefreshViews.contains(child))
                    mNeedRefreshViews.remove(child);
                    child.setId(-1);
                    detachViewFromParent(child);
                    mRecycleBin.addScrapView(((NewBaseItem) child).mViewType, child);
                    mFirstPostion++;
                }
            }
        }
        if (absIncrementalDeltaX >= (mChildWidth - mInitOffset))
        {
            fillGap(right);
        }
    }

    private void fillGap(boolean right)
    {
        if (getChildCount() > maxScreenChildCount)
            return;
        if (right)
        {
            fillLeft();
        } else
        {
            fillRight();
        }
    }

    private View obtainView(int position, boolean[] isScrap)
    {
        isScrap[0] = false;
        View scrapView;
        scrapView = mRecycleBin.getScrapView(position);
        View child;
        if (scrapView != null)
        {
            child = mAdapter.getView(position, scrapView, this);
            if (child != scrapView)
            {
                mRecycleBin.addScrapView(((NewBaseItem) scrapView).mViewType, scrapView);
            } else
            {
                isScrap[0] = true;
            }
        } else
        {
            child = mAdapter.getView(position, null, this);
        }
        return child;
    }

    private void fillRight()
    {
        Log.v("lgx", "fillRight");
        int leftMargin = 0;
        boolean isLastRows = (mLastPosition + 1) % mNumRows == 0 ? true : false;
        int topMargin = 0;
        View preView = this.findViewById(mLastPosition);
        if (preView == null)
            return;
        LayoutParams p = (LayoutParams) preView.getLayoutParams();
        if (isLastRows)
        {
            topMargin = 0;
            leftMargin = p.leftMargin + (mChildWidth + mHorizontalSpace);
        } else
        {
            topMargin = p.topMargin + (mChildHeight + mVerticalSpace);
            leftMargin = p.leftMargin;
        }
        for (int i = 0; i < mNumRows; i++)
        {
            if (mLastPosition >= mAdapter.getCount() - 1)
            {
                return;
            }
            // 现在添加的视图时当前子视图后面，所以位置+1
            mLastPosition++;
            // 数据和视图通过Adapter适配，此处从Adapter获取视图。
            // 第二个参数传入复用的View对象，先出入null，之后再添加View对象复用机制
            View newBottomChild;
            newBottomChild = obtainView(mLastPosition, mIsScrap);
            newBottomChild.setOnKeyListener(this);
            newBottomChild.setOnClickListener(this);
            newBottomChild.setOnFocusChangeListener(this);
            LayoutParams params = new LayoutParams(mChildWidth,
                    mChildHeight);
            params.leftMargin = leftMargin;
            params.topMargin = topMargin;
            topMargin = topMargin + mChildHeight + mVerticalSpace;
            Log.v("lgx", "mIsScrap[0]------> " + mIsScrap[0]);
            if (mIsScrap[0])
            {
                attachViewToParent(newBottomChild, -1, params);
                requestLayout();
            } else
            {
                addView(newBottomChild, params);
            }
            mViewLists.put(mLastPosition, newBottomChild);
            mNeedRefreshViews.add(newBottomChild);
            Log.v("lgx", "downToAddViews position --------> " + newBottomChild.getId());
        }
    }

    private void fillLeft()
    {
        Log.v("lgx", "fillLeft");
        int topMargin = (mNumRows - 1) * (mChildHeight + mVerticalSpace);
        View firstView = this.findViewById(mFirstPostion);
        if (firstView == null)
            return;
        LayoutParams p = (LayoutParams) firstView.getLayoutParams();
        int leftMargin = p.leftMargin - (mChildWidth + mHorizontalSpace);
        for (int i = 0; i < mNumRows; i++)
        {
            if (mFirstPostion <= 0)
            {
                return;
            }
            // 现在添加的视图时当前子视图前面，所以位置-1
            mFirstPostion--;

            View newTopChild;
            newTopChild = obtainView(mFirstPostion, mIsScrap);
            newTopChild.setOnKeyListener(this);
            newTopChild.setOnClickListener(this);
            newTopChild.setOnFocusChangeListener(this);
            LayoutParams params = new LayoutParams(mChildWidth, mChildHeight);
            params.leftMargin = leftMargin;
            params.topMargin = topMargin;
            newTopChild.setLayoutParams(params);
            topMargin -= mChildHeight + mVerticalSpace;
            Log.v("lgx", "mIsScrap[0]------> " + mIsScrap[0]);
            if (mIsScrap[0])
            {
                attachViewToParent(newTopChild, 0, params);
                requestLayout();
            } else
            {
                addView(newTopChild, params);
            }
            mViewLists.put(mFirstPostion, newTopChild);
            mNeedRefreshViews.add(newTopChild);
            Log.v("lgx", "upToAddViews position --------> " + newTopChild.getId());
        }
    }

    @Override
    public void computeScroll()
    {
        Log.e("lgx", "computeScroll mOverScroller.computeScrollOffset()  == " + mOverScroller
                .computeScrollOffset());
        Log.e("lgx", "doScroll == " + doScroll);
        if (mOverScroller.computeScrollOffset())
        {
            int currX = mOverScroller.getCurrX();
            int currY = mOverScroller.getCurrY();
            int incrementalDeltaX = mOverScroller.getStartX() - currX;
            int temp = mOverScroller.getFinalY() - currY;
            trackMotionScroll(incrementalDeltaX);
            super.scrollTo(currX, currY);
        } else
        {
            if (doScroll)
            {
                this.postDelayed(new Runnable()
                {
                    @SuppressWarnings("rawtypes")
                    @Override
                    public void run()
                    {
                        int size = mNeedRefreshViews.size();
                        for (int i = 0; i < size; i++)
                        {
                            NewAdapterItem item = (NewAdapterItem) mNeedRefreshViews.get(i);
                            if (item != null)
                            {
                                item.refreshItemUI();
                            }
                        }
                        mNeedRefreshViews.clear();
                    }
                }, 200);

                if (mScrollListener != null)
                    mScrollListener.onHScrollEnd(this, mFirstPostion, mLastPosition);
            }
        }
    }

    private void fillRightFirstScreen()
    {
        int count = mAdapter.getCount();
        int topMargin = 0;
        int leftMargin = 0;
        int num = (mGridWidth / (mChildWidth + mHorizontalSpace) + 2) * mNumRows;
        maxScreenChildCount = num + mNumRows;
        count = Math.min(count, num);
        mLastPosition = count - 1;
        mInitOffset = mGridWidth - mOneScreenLineCount * (mChildWidth + mHorizontalSpace);
        if (mFirstPostion == 0)
        {
            for (int i = 0; i < count; i++)
            {
                View view = mAdapter.getView(i, null, this);
                view.setOnClickListener(this);
                view.setOnKeyListener(this);
                view.setOnFocusChangeListener(this);
                LayoutParams params = new LayoutParams(mChildWidth,
                        mChildHeight);
                params.leftMargin = leftMargin;
                params.topMargin = topMargin;
                topMargin = topMargin + mChildHeight + mVerticalSpace;
                if ((i + 1) % mNumRows == 0)
                {
                    leftMargin = leftMargin + mChildWidth + mHorizontalSpace;
                    topMargin = 0;
                }
                view.setLayoutParams(params);
                addView(view);
                mViewLists.put(i, view);
                mNeedRefreshViews.add(view);
            }
        } else
        {
            mLastPosition = mFirstPostion + count - 1;
            if (mLastPosition >= (mAdapter.getCount() - 1))
            {
                count = mAdapter.getCount() - mFirstPostion;
                mLastPosition = mFirstPostion + count - 1;
            }
            for (int i = 0; i < count; i++)
            {
                View view = mAdapter.getView((mFirstPostion + i),
                        mRecycleBin.getScrapView((mFirstPostion + i)), this);
                view.setOnClickListener(this);
                view.setOnKeyListener(this);
                view.setOnFocusChangeListener(this);
                LayoutParams params = new LayoutParams(mChildWidth,
                        mChildHeight);
                params.leftMargin = leftMargin;
                params.topMargin = topMargin;
                topMargin = topMargin + mChildHeight + mVerticalSpace;
                if ((i + 1) % mNumRows == 0)
                {
                    leftMargin = leftMargin + mChildWidth + mHorizontalSpace;
                    topMargin = 0;
                }
                view.setLayoutParams(params);
                addView(view);
                mViewLists.put((mFirstPostion + i), view);
                mNeedRefreshViews.add(view);
            }
            fillLeft();
        }
        this.post(new Runnable()
        {
            @SuppressWarnings("rawtypes")
            @Override
            public void run()
            {
                int size = mNeedRefreshViews.size();
                for (int i = 0; i < size; i++)
                {
                    NewAdapterItem item = (NewAdapterItem) mNeedRefreshViews.get(i);
                    if (item != null)
                        item.refreshItemUI();
                }
                mNeedRefreshViews.clear();
            }
        });
    }

    private boolean isScrollInMiddle = false;

    public void setScrollInMiddle(boolean inMiddle)
    {
        isScrollInMiddle = inMiddle;
    }

    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect)
    {
        if (getChildCount() == 0) return 0;

        int width = getWidth();
        int screenLeft = getScrollX();
        int screenRight = screenLeft + width;

        if (isScrollInMiddle)
        {
            width = getWidth() / 2;
            screenLeft = getScrollX() + width - rect.width() / 2;
            screenRight = screenLeft + rect.width();
        }

        int fadingEdge = getHorizontalFadingEdgeLength();

        // leave room for left fading edge as long as rect isn't at very left
        if (rect.left > 0)
        {
            screenLeft += fadingEdge;
        }

        // leave room for right fading edge as long as rect isn't at very right
        if (rect.right < getChildAt(0).getWidth())
        {
            screenRight -= fadingEdge;
        }

        int scrollXDelta = 0;

        if (rect.right > screenRight && rect.left > screenLeft)
        {
            // need to move right to get it in view: move right just enough so
            // that the entire rectangle is in view (or at least the first
            // screen size chunk).

            if (rect.width() > width)
            {
                // just enough to get screen size chunk on
                scrollXDelta += (rect.left - screenLeft);
            } else
            {
                // get entire rect at right of screen
                scrollXDelta += (rect.right - screenRight);
            }

            // make sure we aren't scrolling beyond the end of our content
//            int right = getChildAt(0).getRight();
//            int distanceToRight = right - screenRight;
//            scrollXDelta = Math.min(scrollXDelta, distanceToRight);

        } else if (rect.left < screenLeft && rect.right < screenRight)
        {
            // need to move right to get it in view: move right just enough so that
            // entire rectangle is in view (or at least the first screen
            // size chunk of it).

            if (rect.width() > width)
            {
                // screen size chunk
                scrollXDelta -= (screenRight - rect.right);
            } else
            {
                // entire rect at left
                scrollXDelta -= (screenLeft - rect.left);
            }

            // make sure we aren't scrolling any further than the left our content
//            scrollXDelta = Math.max(scrollXDelta, -getScrollX());
            return scrollXDelta;
        }
        if (scrollXDelta > 0)
            scrollXDelta += scrollOffset;
        else if (scrollXDelta < 0)
            scrollXDelta -= scrollOffset;
        return scrollXDelta;
    }

    private void rememberSyncState()
    {
        int moveRow = 0;
        int scrollSize = 0;
        if (mOldItemCount > mItemCount)
        {
            //当前最后一个如果大于总数，需要删除多余的，否则不用处理
            if (mLastPosition > (mItemCount - 1))
            {
                if (!mOverScroller.isFinished())
                {
                    return;
                }
                int count = mLastPosition - (mItemCount - 1);
                for (int i = 0; i < count; i++)
                {
                    View lastView = mViewLists.get(mLastPosition);
                    lastView.setOnClickListener(null);
                    lastView.setOnKeyListener(null);
                    lastView.setOnFocusChangeListener(null);
                    mViewLists.remove(mLastPosition);
                    if (mNeedRefreshViews.contains(lastView))
                        mNeedRefreshViews.remove(lastView);
                    lastView.setId(-1);
                    detachViewFromParent(lastView);
                    mRecycleBin.addScrapView(((NewBaseItem) lastView).mViewType, lastView);
                    //删除了一行后，自动往上滚一行
                    if (mLastPosition % mNumRows == 0)
                    {
                        moveRow++;
                    }
                    mLastPosition--;
                }
                scrollSize = -(mChildWidth + mHorizontalSpace) * moveRow;
                if (scrollSize == 0 || mLastPosition <= 0)
                    requestLayout();
//                for (int i = 0; i < moveRow; i++)
//                {
//                    fillUp();
//                }
            }
        } else if (mOldItemCount < mItemCount)
        {
            int count = this.getChildCount();
            if (count < (maxScreenChildCount + mNumRows))
                fillRight();
        }
        for (int i = mFirstPostion; i <= mLastPosition; i++)
        {
            @SuppressWarnings("rawtypes")
            NewAdapterItem item = (NewAdapterItem) mAdapter.getView(i, mViewLists.get(i), null);
            item.refreshItemUI();
        }
        if (getFirstPosition() == 0)
            scrollSize = 0;
        if (scrollSize != 0)
            doScrollX(scrollSize, 50);
    }

    class AdapterDataSetObserver extends DataSetObserver
    {
        @Override
        public void onChanged()
        {
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (mOldItemCount == 0 && mItemCount > 0)
            {
                fillRightFirstScreen();
            } else
            {
                if (mOldItemCount == mItemCount)
                {
                    for (int i = mFirstPostion; i <= mLastPosition; i++)
                    {
                        @SuppressWarnings("rawtypes")
                        NewAdapterItem item = (NewAdapterItem) mAdapter.getView(i, mViewLists.get(i),
                                null);
                        item.refreshItemUI();
                    }
                } else
                {
                    rememberSyncState();
                }
            }
        }
    }

    class RecycleBin
    {
        private int mViewTypeCount;
        private ArrayList<View>[] mCachedItemViews;
        private ArrayList<View> mCurrentScrap;

        @SuppressWarnings("unchecked")
        public void setViewTypeCount(int viewTypeCount)
        {
            if (viewTypeCount < 1)
            {
                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
            }
            ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
            for (int i = 0; i < viewTypeCount; i++)
            {
                scrapViews[i] = new ArrayList<View>();
            }
            mCurrentScrap = scrapViews[0];
            mViewTypeCount = viewTypeCount;
            mCachedItemViews = scrapViews;
        }

        public void clear()
        {
            if (mViewTypeCount == 1)
            {
                mCurrentScrap.clear();
            } else
            {
                final int typeCount = mViewTypeCount;
                for (int i = 0; i < typeCount; i++)
                {
                    mCachedItemViews[i].clear();
                }
            }
        }

        public void addScrapView(Integer type, View view)
        {
            if (mViewTypeCount == 1)
            {
                mCurrentScrap.add(view);
            } else
            {
                mCachedItemViews[type].add(view);
            }
        }

        public View getScrapView(int position)
        {
            ArrayList<View> scrapViews;
            if (mViewTypeCount == 1)
            {
                scrapViews = mCurrentScrap;
                int size = scrapViews.size();
                if (size > 0)
                    return scrapViews.remove(size - 1);
                else
                    return null;
            } else
            {
                int whichScrap = mAdapter.getItemViewType(position);
                if (whichScrap >= 0 && whichScrap < mCachedItemViews.length)
                {
                    scrapViews = mCachedItemViews[whichScrap];
                    int size = scrapViews.size();
                    if (size > 0)
                    {
                        return scrapViews.remove(size - 1);
                    }
                }
            }
            return null;
        }
    }

    @Override
    public void onFocusChange(View v, boolean hasFocus)
    {
        if (hasFocus)
        {
            mCurSelectPosition = v.getId();
            if (mItemSelectListener != null)
                mItemSelectListener.onHItemSelected(this, v, mCurSelectPosition);
        } else
        {
            if (mItemSelectListener != null)
                mItemSelectListener.onHItemUnSelected(this, v);
        }
    }

    @Override
    public void onClick(View v)
    {
        if (mItemClickListener != null)
            mItemClickListener.onHItemClick(this, v, v.getId());
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event)
    {
        if (event.getAction() == KeyEvent.ACTION_DOWN)
        {
            int curPosition = v.getId();
            switch (keyCode)
            {
                case KeyEvent.KEYCODE_DPAD_UP:
                    doScroll = false;
                    if (mNumRows == 1 && mItemOnKeyListener != null)
                    {
                        return mItemOnKeyListener.onHBorderItemOnKeyEvent(v, keyCode, curPosition);
                    } else
                    {
                        if ((curPosition % mNumRows) == 0 && mItemOnKeyListener != null)
                        {
                            return mItemOnKeyListener.onHBorderItemOnKeyEvent(v, keyCode,
                                    curPosition);
                        }
                    }
                    break;

                case KeyEvent.KEYCODE_DPAD_DOWN:
                    doScroll = false;
                    if (mNumRows == 1 && mItemOnKeyListener != null)
                    {
                        return mItemOnKeyListener.onHBorderItemOnKeyEvent(v, keyCode, curPosition);
                    } else
                    {
                        if ((curPosition + 1) % mNumRows == 0
                                || curPosition == mAdapter.getCount() - 1)
                        {
                            if (mItemOnKeyListener != null)
                                return mItemOnKeyListener.onHBorderItemOnKeyEvent(v, keyCode,
                                        curPosition);
                        }
                    }
                    break;

//                case KeyEvent.KEYCODE_DPAD_LEFT:
//                    if (getColumn(curPosition) == 1 && mItemOnKeyListener != null)
//                        return mItemOnKeyListener.onHBorderItemOnKeyEvent(v, keyCode, curPosition);
//                    break;

//                case KeyEvent.KEYCODE_DPAD_RIGHT:
//                    if (getColumn(curPosition) == getColumn(mAdapter.getCount() - 1)
//                            && mItemOnKeyListener != null)
//                        return mItemOnKeyListener.onHBorderItemOnKeyEvent(v, keyCode, curPosition);
//                    break;

                default:
                    if (mItemOnKeyListener != null)
                        return mItemOnKeyListener.onHItemOnKeyEvent(v, keyCode, curPosition);
                    break;
            }
        }
        return false;
    }

}
